1 using UnityEngine;
2 using ThreeDPool.EventHandlers;
3 using ThreeDPool.Managers;
4
5 namespace ThreeDPool.Controllers
6 {
7 public class CueBallController : MonoBehaviour
8 {
9 public enum CueBallType
10 {
11 White = 0,
12 Yellow,
13 Blue,
14 Red,
15 Purple,
16 Orange,
17 Green,
18 Burgandy,
19 Black,
20 Striped_Yellow,
21 Striped_Blue,
22 Striped_Red,
23 Striped_Purple,
24 Striped_Orange,
25 Striped_Green,
26 Striped_Burgandy,
27 }
28
29 [SerializeField]
30 float _force = 30f;
31
32 [SerializeField]
33 CueBallType _ballType = CueBallType.White;
34
35 // keep track of the current event
36 private CueBallActionEvent.States _currState;
37
38 private Vector3 _initialPos;
39
40 // flag that diffrentiates between the ball that is pocketed in the current turn versus ball that is pocketed in the earlier turn
41 // this helps in scoring
42 public bool IsPocketedInPrevTurn;
43
44 public CueBallType BallType { get { return _ballType; } }
45
46
47 private void Start()
48 {
49 // record the intial position so that they could be placed in their original position if it goes out of table
50 _initialPos = transform.position;
51
52 EventManager.Subscribe(typeof(CueBallActionEvent).Name, OnCueBallEvent);
53 EventManager.Subscribe(typeof(GameStateEvent).Name, OnGameStateEvent);
54 }
55
56 private void OnDestroy()
57 {
58 EventManager.Unsubscribe(typeof(CueBallActionEvent).Name, OnCueBallEvent);
59 EventManager.Unsubscribe(typeof(GameStateEvent).Name, OnGameStateEvent);
60 }
61
62 private void OnCueBallEvent(object sender, IGameEvent gameEvent)
63 {
64 CueBallActionEvent actionEvent = (CueBallActionEvent)gameEvent;
65 switch(actionEvent.State)
66 {
67 case CueBallActionEvent.States.Stationary:
68 {
69 // change the curr state to default now
70 _currState = CueBallActionEvent.States.Default;
71 }
72 break;
73 }
74 }
75
76 private void OnGameStateEvent(object sender, IGameEvent gameEvent)
77 {
78 GameStateEvent gameStateEvent = (GameStateEvent)gameEvent;
79 switch (gameStateEvent.GameState)
80 {
81 case GameStateEvent.State.Play:
82 {
83 PlaceBallInInitialPos();
84 }
85 break;
86 }
87 }
88
89 private void OnTriggerEnter(Collider collider)
90 {
91 CueController cueController = collider.gameObject.transform.parent.GetComponent<CueController>();
92
93 // confirm if the ball is actually hit by a ball
94 if (cueController != null)
95 {
96 // ball is hit by the cue
97 if (_ballType == CueBallType.White)
98 {
99 // notify that the ball is hit
100 EventManager.Notify(typeof(CueBallActionEvent).Name, this, new CueBallActionEvent() { State = CueBallActionEvent.States.Striked });
101
102 // set the current state
103 _currState = CueBallActionEvent.States.Striked;
104
105 // whats the force gathered to hit
106 float forceGatheredToHit = cueController.ForceGatheredToHit;
107
108 // set the ball rolling with gathered force
109 OnStriked(forceGatheredToHit);
110 }
111 }
112 }
113
114 private void OnCollisionEnter(Collision collision)
115 {
116 if (collision.gameObject.layer == LayerMask.NameToLayer("Floor"))
117 {
118 Debug.Log("Oncollision" + collision.gameObject.name);
119
120 // potted information is detected by PocketCollider
121 // if ball got potted,
122 // check if the ball potted is a cue ball, if yes then player looses the point and the cue is placed back in the table
123 // if not then its a point to the current player
124 GameManager.Instance.AddToBallHitOutList(this);
125 }
126 }
127
128 /// <summary>
129 /// ball goes through various lifecycle from default, placing to the coming back to stationary state after been striked
130 /// </summary>
131 private void FixedUpdate()
132 {
133 Rigidbody rigidbody = gameObject.GetComponent<Rigidbody>();
134 if ((_currState == CueBallActionEvent.States.Placing) && rigidbody.IsSleeping())
135 {
136 _currState = CueBallActionEvent.States.Default;
137 }
138 else if ((_currState == CueBallActionEvent.States.Default) && (!rigidbody.IsSleeping()))
139 {
140 // number of balls striked in Play mode
141 if (GameManager.Instance.CurrGameState == GameManager.GameState.Play)
142 GameManager.Instance.NumOfBallsStriked++;
143
144 _currState = CueBallActionEvent.States.Striked;
145 }
146 else if ((_currState == CueBallActionEvent.States.Striked) && (!rigidbody.IsSleeping()))
147 {
148 _currState = CueBallActionEvent.States.InMotion;
149 }
150 else if ((_currState == CueBallActionEvent.States.Striked) && (rigidbody.IsSleeping()))
151 {
152 _currState = CueBallActionEvent.States.InMotion;
153 }
154 else if ((_currState == CueBallActionEvent.States.InMotion) && rigidbody.IsSleeping())
155 {
156 // ball is come to rest after been striked, check for the status
157 GameManager.Instance.ReadyForNextRound();
158
159 _currState = CueBallActionEvent.States.Stationary;
160 }
161 else
162 {
163 // do nothing
164 }
165 }
166
167 /// <summary>
168 /// OnStriked by Cue
169 /// </summary>
170 /// <param name="forceGathered">amount of force applied on the cue ball</param>
171 private void OnStriked(float forceGathered)
172 {
173 // only apply force on the white ball
174 if (_ballType == CueBallType.White)
175 {
176 GameManager.Instance.NumOfBallsStriked++;
177
178 Rigidbody rigidBody = gameObject.GetComponent<Rigidbody>();
179 rigidBody.AddForce(Camera.main.transform.forward * _force * forceGathered, ForceMode.Force);
180 }
181 }
182
183 /// <summary>
184 /// this function is called by the pockets collider in the pool table when a cue ball touches it
185 /// </summary>
186 public void BallPocketed()
187 {
188 GameManager.Instance.AddToBallPocketedList(this);
189 }
190
191 /// <summary>
192 /// this function is called only while in practise mode
193 /// </summary>
194 public void PlaceBallInPosWhilePractise()
195 {
196 PlaceBallInInitialPos();
197
198 EventManager.Notify(typeof(CueBallActionEvent).Name, this, new CueBallActionEvent() { State = CueBallActionEvent.States.Stationary });
199 }
200
201 /// <summary>
202 /// this function is been called whenever there is a reset of p
203 /// </summary>
204 public void PlaceBallInInitialPos()
205 {
206 // place it a bit from top so that balls dont get placed within each other
207 transform.position = new Vector3(_initialPos.x, _initialPos.y + 0.2f, _initialPos.z);
208
209 // this flag resets after the completion of one complete round,
210 // since the function is reused, and whenever there is a reset, it is safe to reset the flag here
211 IsPocketedInPrevTurn = false;
212
213 _currState = CueBallActionEvent.States.Placing;
214 GameManager.Instance.NumOfBallsStriked = 0;
215 }
216 }
217 }